﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Globalization;
using System.Reflection;
using System.Collections;
using ProgNetComponents.Classes;

namespace ProgNetComponents.Design.TypeConverters
{
    /// <summary>
    /// CollectionItemTypeConverter
    /// </summary>
    /// <typeparam name="T">Collection item type</typeparam>
    public abstract class CollectionItemTypeConverter<T> : ExpandableObjectConverter
    {
        /// <summary>
        /// Returns whether this converter can convert the object to the specified type, using the specified context.
        /// </summary>
        /// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext" /> that provides a format context.</param>
        /// <param name="destinationType">A <see cref="T:System.Type" /> that represents the type you want to convert to.</param>
        /// <returns>
        /// true if this converter can perform the conversion; otherwise, false.
        /// </returns>
        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            return ((destinationType == typeof(InstanceDescriptor)) || base.CanConvertTo(context, destinationType));
        }
        /// <summary>
        /// Converts the given value object to the specified type, using the specified context and culture information.
        /// </summary>
        /// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext" /> that provides a format context.</param>
        /// <param name="culture">A <see cref="T:System.Globalization.CultureInfo" />. If null is passed, the current culture is assumed.</param>
        /// <param name="value">The <see cref="T:System.Object" /> to convert.</param>
        /// <param name="destinationType">The <see cref="T:System.Type" /> to convert the <paramref name="value" /> parameter to.</param>
        /// <returns>
        /// An <see cref="T:System.Object" /> that represents the converted value.
        /// </returns>
        /// <exception cref="System.ArgumentNullException">destinationType</exception>
        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
        {
            if (destinationType == null)
            {
                throw new ArgumentNullException("destinationType");
            }
            if (destinationType.Equals(typeof(InstanceDescriptor)))
            {
                return this.GetInstanceDescriptor(value);
            }
            return base.ConvertTo(context, culture, value, destinationType);
        }

        /// <summary>
        /// Gets the instance descriptor.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        protected InstanceDescriptor GetInstanceDescriptor(object value)
        {
            ConstructorInfo member = GetConstructor((T)value);
            object[] arguments = GetConstructorParamters((T)value);

            return new InstanceDescriptor(member, arguments);
        }
        /// <summary>
        /// Gets the constructor.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <returns></returns>
        protected abstract ConstructorInfo GetConstructor(T item);
        /// <summary>
        /// Gets the constructor paramters.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <returns></returns>
        protected abstract object[] GetConstructorParamters(T item);
    }

    /// <summary>
    /// Advanced CollectionItemTypeConverter
    /// </summary>
    /// <typeparam name="T">Collection item type</typeparam>
    public abstract class CollectionItemTypeConverterAdv<T> : ExpandableObjectConverter
    {
        /// <summary>
        /// Returns whether this converter can convert the object to the specified type, using the specified context.
        /// </summary>
        /// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext" /> that provides a format context.</param>
        /// <param name="destinationType">A <see cref="T:System.Type" /> that represents the type you want to convert to.</param>
        /// <returns>
        /// true if this converter can perform the conversion; otherwise, false.
        /// </returns>
        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            return ((destinationType == typeof(InstanceDescriptor)) || base.CanConvertTo(context, destinationType));
        }
        /// <summary>
        /// Converts the given value object to the specified type, using the specified context and culture information.
        /// </summary>
        /// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext" /> that provides a format context.</param>
        /// <param name="culture">A <see cref="T:System.Globalization.CultureInfo" />. If null is passed, the current culture is assumed.</param>
        /// <param name="value">The <see cref="T:System.Object" /> to convert.</param>
        /// <param name="destinationType">The <see cref="T:System.Type" /> to convert the <paramref name="value" /> parameter to.</param>
        /// <returns>
        /// An <see cref="T:System.Object" /> that represents the converted value.
        /// </returns>
        /// <exception cref="System.ArgumentNullException">destinationType</exception>
        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
        {
            if (destinationType == null)
            {
                throw new ArgumentNullException("destinationType");
            }
            if (destinationType.Equals(typeof(InstanceDescriptor)))
            {
                return this.GetInstanceDescriptor(value);
            }
            return base.ConvertTo(context, culture, value, destinationType);
        }

        /// <summary>
        /// Gets the instance descriptor.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        protected InstanceDescriptor GetInstanceDescriptor(object value)
        {
            TypedObjectCollection arguments = GetConstructorParamters((T)value);
            
            ConstructorInfo member = typeof(T).GetConstructor(arguments.Types);
            return new InstanceDescriptor(member, arguments.Objects);
        }

        /// <summary>
        /// Gets the constructor paramters.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <returns></returns>
        protected abstract TypedObjectCollection GetConstructorParamters(T item);
    }
}
